import { RoleActionRepo, UserRepo } from '@/entities'
import { IBodyUser, IPagesOption, IUserinfo } from '@/interface'
import { CoreException, CoreExceptionType, NRedis } from '@/modules'
import { RType } from '@/modules/redis/interface'
import { sleep, snowflake, timestampFormat } from '@/util'
import { forwardRef, Inject, Injectable, OnModuleInit } from '@nestjs/common'
import { chain } from 'mathjs'
import { UserLoginService } from './login.service'
import { RoleService } from './role.service'

@Injectable()
export class UserService implements OnModuleInit {
    constructor(
        private readonly userRepo: UserRepo,
        private readonly redis: NRedis,
        private readonly roleActionRepo: RoleActionRepo,
        private readonly roleService: RoleService,
        @Inject(forwardRef(() => UserLoginService))
        private readonly userLoginService: UserLoginService,
    ) {}

    public async onModuleInit() {
        const exiting = await this.userRepo.findOne({ phone: '15091156066' })
        if (exiting) {
            exiting.userid = 'system'
            exiting.companyid = '0000'
            exiting.ut = timestampFormat()
            exiting.ed = 'system'
            await this.userRepo.save(exiting)
        } else {
            await this.userRepo.save(
                this.userRepo.create({
                    userid: 'system',
                    companyid: '0000',
                    phone: '1509156066',
                    name: '陈豪',
                    sex: 1,
                    ct: timestampFormat(),
                    au: 'system',
                }),
            )
        }
    }

    /**
     * @description: 获取用户信息【脱敏】
     * @param {IUserinfo} option
     * @return {*}
     */
    public async getOneWithoutSensitive(option: IUserinfo) {
        const user = await this.getOne(option)
        if (user) {
            delete user.account
            delete user.password
            delete user.phone
            delete user.userno
        }
        return user
    }

    /**
     * @description: 获取用户信息【含敏感】
     * @param {IUserinfo} option
     * @return {*}
     */
    public async getOne(option: IUserinfo) {
        const { id, userid, phone, account } = option
        const user: IUserinfo = await this.userRepo
            .createQueryBuilder('u')
            .where('u.id=:id', { id })
            .orWhere('u.userid=:userid', { userid })
            .orWhere('u.phone=:phone', { phone })
            .orWhere('u.account=:account', { account })
            .getOne()
        if (user) {
            user.role = await this.roleService.queryActionListByUserid(userid)
        }
        return user
    }

    /**
     * 批量查询【脱敏】
     * @param userids
     */
    public async getMany(userids: string[]) {
        const users = []
        for await (const userid of userids) {
            users.push(await this.getOneWithoutSensitive({ userid }))
        }
        return users
    }

    /**
     * @description: 生成用户userid
     * @return {string}
     */
    private async getUserUserid() {
        if (await this.redis.get(RType.MAXCODE_USER_LOCK)) {
            await sleep(500)
            return await this.getUserUserid()
        }
        await this.redis.set(RType.MAXCODE_USER_LOCK, 1, 5)
        let maxUserid = await this.redis.get(RType.MAXCODE_USER)
        if (!maxUserid) {
            const user = await this.userRepo.createQueryBuilder().orderBy('userid', 'DESC').getOne()
            maxUserid = chain(user?.userid || 1000)
                .add(1)
                .done()
        } else {
            maxUserid = chain(maxUserid).add(1).done()
        }
        await this.redis.set(RType.MAXCODE_USER, maxUserid, 60 * 60 * 24 * 30)
        await this.redis.del(RType.MAXCODE_USER_LOCK)
        return maxUserid
    }

    /**
     * @description: 更新用户，不存在则新增【返回信息带敏感】
     * @param {IUserinfo} option
     * @param token
     * @return {*}
     */
    public async updateUserByUserid(option: IUserinfo, token?: string) {
        const now = timestampFormat()
        delete option.id
        if (!option.userid) {
            option.userid = await this.getUserUserid()
            await this.userRepo.save(
                this.userRepo.create({
                    ...option,
                    name: option.name || `游客_${option.phone.slice(-4)}`,
                    ct: now,
                    ut: now,
                }),
            )
            // await this.roleService.setRole(option.userid, 'user')
        } else {
            await this.userRepo.update({ userid: option.userid }, { ...option, ut: now })
        }
        if (token) {
            await this.userLoginService.setToken(token, option.userid)
        }
        return await this.getOne({ userid: option.userid })
    }

    /**
     * @description: 查询我的资料
     * @param {IBodyUser} body
     * @return {*}
     */
    public async getMyInfo(body: IBodyUser) {
        return body.__user
    }

    /**
     * 去除敏感信息
     * @param user
     */
    public removeSensitive(user: IUserinfo) {
        if (user) {
            delete user.account
            delete user.password
            delete user.userno
        }
        return user
    }

    /**
     * 用户管理页
     */
    public async queryCompanyUser(option: IUserinfo & IPagesOption & IBodyUser) {
        const { name, phone, status, companyid, role, current = 1, pageSize = 50 } = option
        const role_all_user = await this.roleActionRepo.findOne({
            userid: option.__user.userid,
            action: 'BASE_USER_COMPANY_EDIT',
        })
        const userBuilder = this.userRepo
            .createQueryBuilder('u')
            .leftJoinAndMapOne('u.company', 'company', 'c', 'c.companyid=u.companyid')
            .leftJoin('role_action', 'ra', 'ra.userid=u.userid')
        if (!role_all_user) {
            userBuilder.andWhere('u.companyid=:companyid', { companyid: option.__user.companyid })
        } else if (companyid) {
            userBuilder.andWhere('u.companyid=:companyid', { companyid })
        }
        if (name) {
            userBuilder.andWhere('u.name like :name', { name: `%${name}%` })
        }
        if (phone) {
            userBuilder.andWhere('u.phone like :phone', { phone: `%${phone}%` })
        }
        if (role) {
            userBuilder.andWhere('ra.action=:role', { role })
        }
        if (!isNaN(Number(status))) {
            userBuilder.andWhere('u.status=:status', { status: Number(status) })
        }
        const [data, total] = await userBuilder
            .skip((current - 1) * pageSize)
            .take(pageSize)
            .getManyAndCount()
        data.map((d) => this.removeSensitive(d))
        return {
            data,
            total,
            current,
            pageSize,
            count: data.length,
        }
    }
    public async saveUser(option: IUserinfo & IBodyUser) {
        const { name, phone, userid, companyid } = option
        const ed = option.__user.name
        const exiting = await this.userRepo.findOne({ phone })
        if (exiting && exiting.userid !== userid) {
            throw new CoreException(CoreExceptionType.ERROR_CORE_PHONE_EXITING)
        }
        if (userid) {
            if (userid === 'system') throw new CoreException(CoreExceptionType.ERROR_CORE_CONFIG_ADMIN_EDIT)
            await this.userRepo.update(
                { userid },
                {
                    name,
                    phone,
                    companyid,
                    ut: timestampFormat(),
                    ed,
                },
            )
        } else {
            await this.userRepo.save(
                this.userRepo.create({
                    name,
                    phone,
                    companyid,
                    userid: snowflake(),
                    ct: timestampFormat(),
                    au: ed,
                }),
            )
        }
        return { message: '保存成功！' }
    }
    public async deleteUser(option: IUserinfo) {
        const { userid } = option
        if (userid === 'system') throw new CoreException(CoreExceptionType.ERROR_CORE_CONFIG_ADMIN_DELETE)
        await this.userRepo.delete({ userid })
        return { message: '删除成功！' }
    }
    public async editStatus(option: IUserinfo & IBodyUser) {
        const { userid, status } = option
        const ed = option.__user.name
        if (userid === 'system') throw new CoreException(CoreExceptionType.ERROR_CORE_CONFIG_ADMIN_DELETE)
        await this.userRepo.update(
            { userid },
            {
                status,
                ed,
                ut: timestampFormat(),
            },
        )
        return { message: '修改成功！' }
    }
}
